package org.atlantis.grosbeak.cable;

import java.util.ArrayList;

import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

import org.atlantis.grosbeak.cable.boult.ActionBoult;
import org.atlantis.grosbeak.cable.boult.InterceptorBoult;
import org.atlantis.grosbeak.castor.Castors;
import org.atlantis.grosbeak.depot.Boult;
import org.atlantis.grosbeak.depot.BoultDepot;
import org.atlantis.grosbeak.depot.DepotException;
import org.atlantis.grosbeak.depot.env.Depoter;
import org.atlantis.grosbeak.depot.loader.JsonLoader;
import org.atlantis.grosbeak.lang.Exceptions;
import org.atlantis.grosbeak.lang.Mirror;
import org.atlantis.grosbeak.lang.Stopwatch;
import org.atlantis.grosbeak.lang.Strings;
import org.atlantis.grosbeak.logger.Logger;
import org.atlantis.grosbeak.logger.LoggerFactory;

/**
 * 初始化监听器。在应用启动时，会对Cable进行初始化。其中用到如下的几个参数：
 * <ul>
 * <li>boults：配置自己的筛选器，而筛选器详细作用可见：{@link org.atlantis.grosbeak.depot.Boult}
 * ，如果有多个筛选器则用逗号分开。</li>
 * <li>configs：JSON配置文件的路径，如果有多个配置文件则用逗号分开。</li>
 * </ul>
 * 其中boults如果没有就不用填写了，但是configs是必须的，因为所有的ActionBean都使托付给Depot容器管理的。<br>
 * 同时注意：如果你需要使用自己的转换器，那么可以在Depot的JSON配置文件中配置一个id为castorConfig的
 * {@link org.atlantis.grosbeak.cable.CastorConfig}类型的对象，这样就可以在Web容器启动时自动加载
 * 自定义转换器。
 * 
 * @author <a href="mailto:seenoevil.cn@gmail.com">SeeNoEvil</a>
 * 
 */
public class CableInitContext implements ServletContextListener {

	private final static Logger logger = LoggerFactory
			.getLogger(CableInitContext.class);

	public void contextInitialized(ServletContextEvent sce) {
		logger.infoFmt("Cable framework is initializing ...");
		ServletContext sc = sce.getServletContext();

		// 获得用户配置的Boult筛选器器，并和Cable所必需的筛选器合并在一起
		String boultNames = sc.getInitParameter("boults");
		ArrayList<Boult> boults = new ArrayList<Boult>();
		boults.add(new ActionBoult());
		boults.add(new InterceptorBoult());
		if (boultNames != null) {
			String[] boultArray = Strings.spliteIgnoreBlank(boultNames, ",");
			for (String boultName : boultArray) {
				try {
					Mirror<?> boult = Mirror.me(Class.forName(boultName));
					boults.add((Boult) boult.fruit());
				} catch (Exception e) {
					throw Exceptions.makeThrow(CableException.class,
							" Unable to instance '%s'.", boultName);
				}
			}
		}

		// 开启秒表
		Stopwatch watch = Stopwatch.begin();
		// 获取所有的Depot配置文件
		String configFiles = sc.getInitParameter("configs");
		// 如果获取不到配置文件，则抛出一个异常
		if (Strings.isBlank(configFiles))
			throw Exceptions.makeThrow(CableException.class,
					"Unable to find any Depot Json configuration files.");

		// 将配置文件字符串切割开来
		String[] configs = Strings.spliteIgnoreBlank(configFiles);

		BoultDepot depot = new Depoter(new JsonLoader(configs), boults
				.toArray(new Boult[0]));

		// 将Depot容器存入到ServletContext中
		sc.setAttribute(BoultDepot.class.getName(), depot);

		// 从Depot容器中获取转换器的配置路径
		CastorConfig cc = null;
		try {
			depot.get(CastorConfig.class, "castorConfig");
		} catch (DepotException e) {
		}
		Castors castors = Castors.me();
		if (cc != null && cc.getConfigs().length > 0) {
			castors.addPath(cc.getConfigs());
		}

		watch.stop();
		String seconds = String.valueOf(watch.getDuration() / 1000);
		logger.infoFmt(
				"Cable framework has initialized. It takes '%s' seconds.",
				seconds);
	}

	public void contextDestroyed(ServletContextEvent sce) {
		logger.infoFmt("Cable framework is destroying ...");
		BoultDepot depot = (BoultDepot) sce.getServletContext().getAttribute(
				BoultDepot.class.getName());
		if (depot != null) {
			depot.destroy();
		}
		logger.infoFmt("Cable framework has destroied.");
	}
}
